package com.huidu.bitrade.service;

import com.alibaba.fastjson2.JSON;
import com.huidu.bitrade.constant.OtcBusinessConstants;
import com.huidu.bitrade.constant.PageModel;
import com.huidu.bitrade.dao.OtcBusinessRealizationDao;
import com.huidu.bitrade.entity.*;
import com.huidu.bitrade.exception.ServiceException;
import com.huidu.bitrade.pagination.Criteria;
import com.huidu.bitrade.pagination.Restrictions;
import com.huidu.bitrade.service.Base.BaseService;
import com.huidu.bitrade.util.DateUtil;
import com.huidu.bitrade.vo.BusinessQuotaVo;
import com.huidu.bitrade.vo.MemberAccountVO;
import com.huidu.bitrade.vo.OtcBusinessRealizationVO;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.text.ParseException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Objects;

@Service
@RequiredArgsConstructor
public class OtcBusinessRealizationService extends BaseService {

    private final JPAQueryFactory queryFactory;
    private final OtcBusinessRealizationDao otcBusinessRealizationDao;
    private final MemberService memberService;
    private final MemberWalletService memberWalletService;


    public Page<OtcBusinessRealizationVO> joinFind(List<Predicate> predicates, PageModel pageModel) {
        List<OrderSpecifier> orderSpecifiers = pageModel.getOrderSpecifiers();
        JPAQuery<OtcBusinessRealizationVO> query = queryFactory.select(
                Projections.fields(OtcBusinessRealizationVO.class,
                        QOtcBusinessRealization.otcBusinessRealization.id.as("id"),
                        QOtcBusinessRealization.otcBusinessRealization.memberId.as("memberId"),
                        QOtcBusinessRealization.otcBusinessRealization.amount.as("amount"),
                        QOtcBusinessRealization.otcBusinessRealization.createTime.as("createTime"),
                        QOtcBusinessRealization.otcBusinessRealization.image.as("image"),
                        QOtcBusinessRealization.otcBusinessRealization.status.as("status"),
                        QOtcBusinessRealization.otcBusinessRealization.payInfo.as("payInfo"),
                        QOtcBusinessRealization.otcBusinessRealization.reason.as("reason"),
                        QOtcBusinessRealization.otcBusinessRealization.auditTime.as("auditTime"),
                        QOtcBusinessRealization.otcBusinessRealization.payTime.as("payTime"),
                        QMember.member.username.as("memberUsername"),
                        QMember.member.realName.as("memberRealName"),
                        QMember.member.mobilePhone.as("phone"),
                        QMember.member.businessDepositStrategy.as("businessDepositStrategy")
                        )
        ).from(QOtcBusinessRealization.otcBusinessRealization, QMember.member).where(predicates.toArray(new Predicate[0]));
        query.orderBy(orderSpecifiers.toArray(new OrderSpecifier[0]));
        List<OtcBusinessRealizationVO> list = query.offset((long) (pageModel.getPageNo() - 1) * pageModel.getPageSize()).limit(pageModel.getPageSize()).fetch();
        list.forEach(vo -> vo.setPayInfo(vo.getPayInfo()));
        long total = query.fetchCount();
        return new PageImpl<>(list, pageModel.getPageable(), total);

    }

    public Page<OtcBusinessRealization> queryByMember(Long memberId, Integer pageNo, Integer pageSize,String startDate, String endDate,Integer status) throws ParseException {
        Sort orders = Criteria.sortStatic("createTime.desc");
        //分页参数
        PageRequest pageRequest = PageRequest.of(pageNo-1, pageSize, orders);
        //查询条件
        Criteria<OtcBusinessRealization> specification = new Criteria<OtcBusinessRealization>();
        specification.add(Restrictions.eq("memberId", memberId, false));
        if(StringUtils.isNotBlank(startDate) && StringUtils.isNotBlank(endDate)){
            specification.add(Restrictions.gte("createTime", DateUtil.YYYY_MM_DD_MM_HH_SS.parse(startDate+" 00:00:00"),false));
            specification.add(Restrictions.lte("createTime",DateUtil.YYYY_MM_DD_MM_HH_SS.parse(endDate+" 23:59:59"),false));
        }
        if(Objects.nonNull(status)){
            specification.add(Restrictions.eq("status", status, false));
        }
        return otcBusinessRealizationDao.findAll(specification, pageRequest);
    }

    public BigDecimal findTodayAmount(Long memberId, int status){
        return otcBusinessRealizationDao.findTodayAmount(memberId,status);
    }

    /**
     * 查询用户历史承兑数量
     * @param memberId
     * @return
     */
    public BigDecimal getAllGain(Long memberId){
        return otcBusinessRealizationDao.getAllGain(memberId);
    }


    public List<OtcBusinessRealization> findByMemberIdAndStatus(Long memberId) {
        return otcBusinessRealizationDao.findByMemberIdAndStatus(memberId);
    }
    /**
     * 获取商家承兑额度
     */
    public BusinessQuotaVo getBusinessQuota(Long memberId){
        Member member = memberService.findOne(memberId);
        Assert.isTrue(Objects.nonNull(member),"未找到此用户");
        if(Objects.isNull(member.getBusinessDepositStrategy())){
            return BusinessQuotaVo.builder().build();
        }
        //查询每日限额
        BusinessDepositStrategy strategy = member.getBusinessDepositStrategy();
        BusinessQuotaVo result = BusinessQuotaVo.builder().everyQuota(strategy.getDailyAmount()).build();
        //查询今日已变现额度
        result.setResidue(result.getEveryQuota().subtract(getDayRealization(memberId)));
        return result;
    }

    /**
     * 查询今日已变现额度
     * @return
     */
    public BigDecimal getDayRealization(Long memberId){
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String start = LocalDateTime.of(LocalDate.now(), LocalTime.MIN).format(formatter);
        String end = LocalDateTime.of(LocalDate.now(), LocalTime.MAX).format(formatter);
        BigDecimal price = otcBusinessRealizationDao.getDayRealization(start,end,memberId);
        return Objects.isNull(price)?BigDecimal.ZERO:price;
    }


    /**
     * 申请变现
     */
    @Transactional(rollbackFor = Exception.class)
    public void apply(Member member,BigDecimal amount,Integer payType) throws ServiceException {
        //判断承兑额度是否足够
        BusinessQuotaVo businessQuotaVo = getBusinessQuota(member.getId());
        if(businessQuotaVo.getResidue().compareTo(amount)<0){
            throw new ServiceException("剩余额度不足："+businessQuotaVo.getResidue());
        }
        //判断余额
        MemberWallet memberWallet = memberWalletService.findByCoinUnitAndMemberId("CNHD",member.getId());
        if(memberWallet.getBalance().compareTo(amount)<0){
            throw new ServiceException("钱包余额不足");
        }
        //冻结钱包余额
        memberWalletService.freezeBalance(memberWallet,amount);
        //添加变现记录
        MemberAccountVO memberAccountVO = new MemberAccountVO();
        memberAccountVO.setRealName(member.getRealName());
        switch (payType){
            case 1:
                memberAccountVO.setAlipay(member.getAlipay());
                break;
            case 2:
                memberAccountVO.setWechatPay(member.getWechatPay());
                break;
            case 3:
                memberAccountVO.setBankInfo(member.getBankInfo());
                break;
        }
        otcBusinessRealizationDao.save(OtcBusinessRealization.builder()
                .memberId(member.getId())
                .payInfo(JSON.toJSONString(memberAccountVO))
                .payType(payType)
                .status(OtcBusinessConstants.DEPOSIT_RECORD_HISTORY)
                .amount(amount).build());
    }


    public OtcBusinessRealization findOne(Long id) {
        return otcBusinessRealizationDao.findById(id).orElse(null);
    }
    public OtcBusinessRealization save(OtcBusinessRealization realization) {
        return otcBusinessRealizationDao.saveAndFlush(realization);
    }
}
